/* -*- c++ -*-
   llvmir.cc - LLVMIR specific code.
   Copyright (C) 2011 Matthew Wahab <mwb.cde@gmail.com>

   This file is part of GCC-LLVMIR.

   GCC-LLVMIR is free software; you can redistribute it and/or modify it under
   the terms of the GNU General Public License as published by the Free
   Software Foundation; either version 3, or (at your option) any later
   version.

   GCC-LLVMIR is distributed in the hope that it will be useful, but WITHOUT ANY
   WARRANTY; without even the implied warranty of MERCHANTABILITY or
   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   for more details.

   You should have received a copy of the GNU General Public License
   along with GCC-LLVMIR; see the file COPYING3.  If not see
   <http://www.gnu.org/licenses/>.  */

/* Driver code for the llvm IR -> gcc frontend. */

#include <iostream>

#include "llvmir.h"
#include "parser.h"
#include "asmparser.h"

namespace llvmir
{
  /*
   * Utility functions.
   */

  /* Allocate an array of strings, with the contents of the
     vector. The number of elements in the array is the same as the
     number of strings in the vector.
   */
  const char** array_of_strseq(const StringSeq& string_seq)
  {
    const char** str_array = 0;
    const unsigned seq_size = string_seq.size();
    unsigned idx = 0;

    str_array = new const char*[seq_size + 1];
    idx = 0;
    for (StringSeq::const_iterator
           itr = string_seq.begin(),
           end = string_seq.end();
         itr != end;
         ++itr)
      {
        const std::string& itr_str = *itr;
        str_array[idx] = itr_str.c_str();
        idx += 1;
      }
    str_array[idx] = NULL;

    return str_array;
  }

  /* Break up a string adding the elements the given vector. options
     seperator is a space, escaped with '\'. */
  static void
  breakup_opt_string(const std::string& option_string,
                     StringSeq& option_seq)
  {
    // The character seperating the options
    const char seperator = ' ';
    const char escape = '\\';
    std::string buff;

    for (size_t idx = 0, size = option_string.length();
         idx < size;
         idx += 1)
      {
        const char curr = option_string[idx];
        if (curr == seperator)
          {
            option_seq.push_back(buff);
            buff.clear();
            continue;
          }
        if (curr == escape)
          {
            if (((idx + 1) < size)
                && (option_string[idx + 1] == seperator))
              {
                // Advance two places.
                idx += 1;
                buff.push_back(seperator);
                continue;
              }
          }
        buff.push_back(curr);
      }
    if (!buff.empty())
      option_seq.push_back(buff);
  }

  /*
   * Global variables
   */

  /* Available parsers. */
  static AsmParser global_asm_parser;
#ifdef LLVMIR_USE_CLANG
  static ClangParser global_clang_parser;
#endif // LLVMIR_USE_CLANG

  /* Table of parsers, in the order that they are to be tested. */
  static Parser* parser_table[] =
    { 
#ifdef LLVMIR_USE_CLANG
      &global_clang_parser,
#endif // LLVMIR_USE_CLANG
      &global_asm_parser,
      0
    };

  /*
   * Global functions
   */

  /* Get the global options */
  Options*
  get_options()
  {
    /* The global options. */
    static Options global_options;

    return &global_options;
  }

  /* Initialise the global options */
  void
  init_options(struct gcc_options *gcc_opts ATTRIBUTE_UNUSED)
  { 
    Options* global_options = get_options();
    global_options->clear();
  }

  /* Finalize the global options. This sets the parser to be used. */
  void
  finalize_options()
  { 
    Options* global_options = get_options();
    global_options->clear();
  }

  /* Get the global context. */
  Context*
  get_context()
  {
    /* The global translation context */
    static Context global_context;

    return &global_context;
  }

  /* Initialise the global context */
  void
  init_context()
  {
    Context* global_context = get_context();
    gcc_assert(!global_context->parser);

    // Set the options.
    global_context->options = get_options();
  }

  /* Finalize the global context */
  void
  finalize_context()
  {
    Context* global_context = get_context();
    global_context->options = 0;

    delete(global_context->parser);
    global_context->parser = 0;
  }

  /* Get the parser that matches the language kind */
  static Parser*
  select_parser(LangKind language)
  {
    switch(language)
      {
      case LANG_ASM:
        return &(global_asm_parser);

#ifdef LLVMIR_USE_CLANG
      case LANG_CLANG:
        return &(global_clang_parser);
#endif // LLVMIR_USE_CLANG

      case LANG_OTHER:
      default:
        return 0;
#if 0
      case LANG_OTHER:
        fatal_error("only LLVM assembler is supported.");
      default:
        return &(global_asm_parser);
#endif
      }
  }

  /* Find a parser that accepts an input file name. */
  static Parser*
  select_parser_by_filename(Context* context ATTRIBUTE_UNUSED,
                            const std::string& filename)
  {
    for (unsigned idx = 0;
         parser_table[idx] != 0;
         idx += 1)
      {
        if (parser_table[idx]->accepts_filename(context, filename))
          return parser_table[idx];
      }
    return 0;
  }

  /* Process the global options. Called after all arguments have been
     parsed. This initializes the context. This sets the parser to be
     used, if the input language has been specified. */
  void
  process_options()
  {
    Options* options = get_options();
    Context* context;
    Parser* parser = 0;
    gcc_assert(options);

    // Initialize the LLVMIR context.
    init_context();
    context = get_context();

    // Choose the parser.
    parser = select_parser(options->language);

    if (parser)
      {
        parser->init(context);
        context->parser = parser;

        // Pass the options to the parser.
        parser->process_options(options);
      }
#if 0
    if (!parser)
      fatal_error("unsupported input language.");

    parser->init(context);
    context->parser = parser;

    // Pass the options to the parser.
    parser->process_options(options);
#endif
  }

  /* Compiler initialisation */
  void
  initialize()
  {
    /* skip */
  }

  /* Finalize compiler */
  void
  finalize()
  {
    /* skip */
  }

  /*
   * Option setting 
   */

  /* Set the input language. */
  bool 
  option_lang(const char* arg, const int lang)
  {
    Options* options = get_options();
    LangKind kind = static_cast<LangKind>(lang);

    if (kind >= LANG_OTHER)
      {
        error ("unknown input language %qs specified", arg);
        return false;
      }

    options->language = kind;
    return true;
  }

  /* Set the options to pass to the llvmir parser. */
  bool
  option_opt(const char* opt_cstr)
  {
    const std::string opt_string = opt_cstr;
    Options* options = get_options();
    breakup_opt_string(opt_string, options->parser_opts);

    return true;
  }

  /* Handle a -I option. */
  bool
  option_include(const char* opt_cstr)
  {
    Options* options = get_options();
    options->include_dirs.push_back(opt_cstr);

    return true;
  }

  /* Handle a -L option. */
  bool
  option_libdir(const char* opt_cstr)
  {
    Options* options = get_options();
    options->library_dirs.push_back(opt_cstr);

    return true;
  }

  /* Set amount of debugging information. */
  bool
  option_dump(const char* opt_cstr ATTRIBUTE_UNUSED,
                     const int value ATTRIBUTE_UNUSED)
  {
    Options* options = get_options();
    options->debug_output = value;

    return true;
  }

  /* Generic debugging option. */
  bool
  option_debug(const char* opt_cstr ATTRIBUTE_UNUSED,
                      const int value ATTRIBUTE_UNUSED)
  {
    Options* options = get_options();
    options->debug_value = value;

    return true;
  }

  /* Parsing */
  bool
  parse_input_files(const char** filenames,
                           const unsigned int num_files,
                           int syntax_only)
  {
    Context* context = get_context();
    bool ret = true;
    bool old_syntax_only = context->options->syntax_only;
    Parser* parser = context->get_parser();

    if (syntax_only)
      context->options->syntax_only = true;
    else 
      context->options->syntax_only = false;

    // compile the files.
    for(unsigned int i = 0; i < num_files; ++i)
      {
        // If the parser wasn't set up when processing the options (by
        // specifying an input language), choose the first parser that
        // accepts the file name.
        if (!parser)
          {
            parser = select_parser_by_filename(context, filenames[i]);
            if (!parser)
              fatal_error("unsupported input file and language not specified.");

            parser->init(context);
            context->parser = parser;

            // Pass the options to the parser.
            parser->process_options(context->options);
          }
        gcc_assert(parser);

        if (!parser->compile_file(context, filenames[i]))
          {
            // Failed to compile the file.
            ret = false;
            break;
          }
      }

    context->options->syntax_only = old_syntax_only;
    return ret;
  }

  /*
   * Method definitions
   */

  /*
   * Class Scope
   */

  /* Destructor */
  Scope::~Scope()
  {
    this->clear();
  }

  /* Clear a scope, deleting all tracked objects. */
  void Scope::clear()
  {
    // Empty the maps.
    type_repr_map.clear();
    value_repr_map.clear();

    // Drop all tracked objects.
    // Delete the objects created for the scope.
    for (TypeReprTracker *itr = type_repr_objects.front(),
           *next_itr = 0;
         itr != 0;
         itr = next_itr)
      {
        TypeRepr* repr = itr->parent;
        next_itr = itr->next();
        delete(repr);
      }

    for (ValueReprTracker *itr = value_repr_objects.front(),
           *next_itr = 0;
         itr != 0;
         itr = next_itr)
      {
        ValueRepr* repr = itr->parent;
        next_itr = itr->next();
        delete(repr);
      }
  }

  /* Look-up a value. */
  ValueRepr* Scope::find_value(const llvm::Value* llvm_value)
  {
    ValueReprMap::iterator itr = 
      value_repr_map.find(llvm_value);

    if (itr != value_repr_map.end())
      {
        ValueRepr* value_repr = (*itr).second;
        return value_repr;
      }
    return 0;
  }

  /* Store a value. */
  ValueRepr* Scope::store_value(const llvm::Value* llvm_value,
                                ValueRepr* value_repr)
  {
    if (llvm_value)
      value_repr_map[llvm_value] = value_repr;
    return value_repr;
  }

  /* Remove a value. */
  void Scope::remove_value(const llvm::Value* llvm_value)
  {
    ValueReprMap::iterator itr = value_repr_map.find(llvm_value);

    if (itr != value_repr_map.end())
      value_repr_map.erase(itr);
  }

  /* Track a value. */
  ValueRepr* Scope::track_value_repr(ValueRepr* value_repr)
  {
    ValueReprTracker* tracker = 0;

    if (!value_repr)
      return 0;
    
    tracker = value_repr->get_tracker();
    if (tracker->list())
      {
        gcc_assert((tracker->list()) == &(value_repr_objects));
        return value_repr;
      }

    value_repr_objects.push_back(tracker);
    return value_repr;
  }

  /* Stop tracking a value. */
  ValueRepr* Scope::ignore_value_repr(ValueRepr* value_repr)
  {
    ValueReprTracker* tracker = 0;

    if (!value_repr)
      return 0;
    
    tracker = value_repr->get_tracker();

    if (value_repr_objects.member(tracker))
      value_repr_objects.remove(tracker);

    return value_repr;
  }

  /* Register value (store and track a value). */
  ValueRepr* Scope::register_value_repr(ValueRepr* value_repr)
  {
    if (!value_repr)
      return 0;
    this->track_value_repr(value_repr);
    this->store_value(value_repr->get_value(), value_repr);
    return value_repr;
  }

  /* Drop value representation (don't store or track a value). */
  void Scope::drop_value_repr(ValueRepr* value_repr)
  {
    if (!value_repr)
      return;
    this->ignore_value_repr(value_repr);
    this->remove_value(value_repr->get_value());
  }

  /* Look-up a type. */
  TypeRepr* Scope::find_type(const llvm::Type* llvm_type)
  {
    TypeReprMap::iterator itr = type_repr_map.find(llvm_type);

    if (itr != type_repr_map.end())
      {
        TypeRepr* type_repr = (*itr).second;
        return type_repr;
      }
    return 0;
  }

  /* Store a type. */
  TypeRepr* Scope::store_type(const llvm::Type* llvm_type,
                              TypeRepr* type_repr)
  {
    if (llvm_type)
      type_repr_map[llvm_type] = type_repr;

    return type_repr;
  }

  /* Remove a type. */
  void Scope::remove_type(const llvm::Type* llvm_type)
  {
    TypeReprMap::iterator itr = type_repr_map.find(llvm_type);

    if (itr != type_repr_map.end())
      type_repr_map.erase(itr);
  }

  /* Track a type. */
  TypeRepr* Scope::track_type_repr(TypeRepr* type_repr)
  {
    TypeReprTracker* tracker = 0;

    if (!type_repr)
      return 0;
    
    tracker = type_repr->get_tracker();
    if (tracker->list())
      {
        gcc_assert((tracker->list()) == &(type_repr_objects));
        return type_repr;
      }

    type_repr_objects.push_back(tracker);
    return type_repr;
  }

  /* Stop tracking a type. */
  TypeRepr* Scope::ignore_type_repr(TypeRepr* type_repr)
  {
    TypeReprTracker* tracker = 0;

    if (!type_repr)
      return 0;
    
    tracker = type_repr->get_tracker();

    if (type_repr_objects.member(tracker))
      type_repr_objects.remove(tracker);

    return type_repr;
  }

  /* Register type (store and track a type). */
  TypeRepr* Scope::register_type_repr(TypeRepr* type_repr)
  {
    if (!type_repr)
      return 0;
    this->track_type_repr(type_repr);
    this->store_type(type_repr->get_type(), type_repr);
    return type_repr;
  }

  /* Drop type representation (don't record or track the type). */
  void Scope::drop_type_repr(TypeRepr* type_repr)
  {
    if (!type_repr)
      return;
    this->ignore_type_repr(type_repr);
    this->remove_type(type_repr->get_type());
  }

  /* Look-up an alias definition, return 0 if not found. */
  const llvm::Value* Scope::find_alias(const llvm::Value* llvm_value)
  {
    AliasMap::iterator itr = llvm_alias_map.find(llvm_value);

    if (itr != llvm_alias_map.end())
      {
        const llvm::Value* llvm_def = (*itr).second;
        return llvm_def;
      }
    return 0;
  }

  /* Store an alias definition. */
  const llvm::Value* Scope::store_alias(const llvm::Value* llvm_value, 
                                         const llvm::Value* llvm_def)
  {
    if (llvm_value && llvm_def)
      llvm_alias_map[llvm_value] = llvm_def;

    return llvm_value;
  }

  /* Remove an alias definition. */
  void Scope::remove_alias(const llvm::Value* llvm_value)
  {
    AliasMap::iterator itr = llvm_alias_map.find(llvm_value);

    if (itr != llvm_alias_map.end())
      llvm_alias_map.erase(itr);
  }

  /* Look-up an alias representation, return 0 if not found. */
  ValueRepr* Scope::find_alias_repr(const llvm::Value* llvm_value)
  {
    AliasReprMap::iterator itr = alias_repr_map.find(llvm_value);

    if (itr != alias_repr_map.end())
      {
        ValueRepr* value_repr = (*itr).second;
        return value_repr;
      }
    return 0;
  }

  /* Store an alias representation. */
  ValueRepr* Scope::store_alias_repr(const llvm::Value* llvm_value, 
                                     ValueRepr* alias_repr)
  {
    if (llvm_value && alias_repr)
      alias_repr_map[llvm_value] = alias_repr;

    return alias_repr;
  }

  /* Remove an alias representation. */
  void Scope::remove_alias_repr(const llvm::Value* llvm_value)
  {
    AliasReprMap::iterator itr = alias_repr_map.find(llvm_value);

    if (itr != alias_repr_map.end())
      alias_repr_map.erase(itr);
  }

  /*
   * Class FieldRepr
   */

  tree FieldRepr::get_tree()
  {
    if (!subtype)
      return NULL_TREE;
    return subtype->get_tree();
  }

  const llvm::Type* FieldRepr::get_type()
  {
    if (!subtype)
      return 0;
    return subtype->get_type();
  }

  /*
   * Class ValueRepr
   */

  /* Destructor (in llvmir.cc) */
  ValueRepr::~ValueRepr()
  {
    if (this->llvm_value_managed_ && this->llvm_value_)
      delete this->llvm_value_;
    
    this->llvm_value_ = 0;
    this->repr_ = 0;
  }

  /*
   * Class InstrRepr
   */

  /* Destructor */
  InstrRepr::~InstrRepr()
  {
    instrs_ = NULL_TREE;
    extra_instrs_ = NULL_TREE;
  }

  /*
   * Class BlockRepr
   */

  /* Destructor */
  BlockRepr::~BlockRepr()
  { 
    extra_end_instrs.clear_content();
  }

  /*
   * Class VarRepr
   */

  /* Destructor */
  VarRepr::~VarRepr()
  { /* skip */ }

  /*
   * Class FunctionRepr
   */

  /* Destructor */
  FunctionRepr::~FunctionRepr()
  { /* skip */ }

  /*
   * Class ObjContext
   */

  /* Destructor */
  ObjContext::~ObjContext()
  { /* skip */ }

  /* Reset a function context. */
  void 
  ObjContext::reset()
  {
    this->src_loc = UNKNOWN_LOCATION;
    this->scope.clear();
  }

  /* Look-up a stored conversion for a value, return 0 if not found. */
  ValueRepr*
  ObjContext::lookup_value(const llvm::Value* llvm_value)
  {
    return scope.find_value(llvm_value);
  }

  /* Register (store and track) a value representation. */
  ValueRepr*
  ObjContext::register_value_repr(ValueRepr* value_repr)
  {
    return scope.register_value_repr(value_repr);
  }

  /* Drop a stored conversion for a value, return 0 if not found. */
  void
  ObjContext::drop_value_repr(ValueRepr* value_repr)
  {
    scope.drop_value_repr(value_repr);
  }
    
  /* Look-up a stored conversion for a type, return 0 if not found. */
  TypeRepr*
  ObjContext::lookup_type(const llvm::Type* llvm_type)
  {
    return scope.find_type(llvm_type);
  }

  /* Register (store and track) a type representation. */
  TypeRepr*
  ObjContext::register_type_repr(TypeRepr* type_repr)
  {
    return scope.register_type_repr(type_repr);
  }

  /* Drop a stored conversion for a type */
  void
  ObjContext::drop_type_repr(TypeRepr* type_repr)
  {
    scope.drop_type_repr(type_repr);
  }

  /*
   * Class FnContext
   */

  /* Cast to a function context */
  FnContext* FnContext::from(ObjContext* obj_context)
  {
    if (!obj_context)
      return 0;

    // The current context must be a function.
    if (obj_context->kind() != ObjContext::FUNCTION)
      {
        error("compiler error (invalid context).");
        gcc_unreachable();
      }

    return static_cast<FnContext*>(obj_context);
  }

  /* Set-up a function context for a new function. */
  void FnContext::set_function(FunctionRepr* fr)
  {
    fn_repr = fr;
    if (fn_repr->result)
      this->register_value_repr(fn_repr->result);
  }

  /* Reset a function context. */
  void FnContext::reset()
  {
    this->ObjContext::reset();
    this->fn_repr = 0;
  }

  /*
   * Class UnitContext
   */

  /* Cast to a translation unit context */
  UnitContext* UnitContext::from(ObjContext* obj_context)
  {
    if (!obj_context)
      return 0;

    // The current context must be a function.
    if (obj_context->kind() != ObjContext::UNIT)
      {
        error("compiler error (invalid context).");
        gcc_unreachable();
      }

    return static_cast<UnitContext*>(obj_context);
  }

  /* Reset a translation unit context. */
  void UnitContext::reset()
  {
    this->ObjContext::reset();
    scope.clear();
    unit_decln_tree = NULL_TREE;
    unit_scope_tree = NULL_TREE;
  }

  /*
   * Class Options
   */

  /* Reset the values to their default values. */
  void
  Options::clear()
  {
    language = LANG_OTHER;

    parser_opts.clear();
    include_dirs.clear();
    library_dirs.clear();
    syntax_only = false;

    debug_output = 0;
    debug_value = 0;
  }

  /*
   * Class Context
   */

  /* Destructor */
  Context::~Context()
  { /* skip */  }

  /* Get the parser to use. */
  Parser* 
  Context::get_parser()
  {
    return parser;
  }

  /* Look-up a stored conversion for a value, return 0 if not found. */
  ValueRepr*
  Context::lookup_value(const llvm::Value* llvm_value)
  {
    ValueRepr* value_repr = 0;
    if (curr_context)
      value_repr = curr_context->lookup_value(llvm_value);
    if (!value_repr)
      value_repr = unit_context.lookup_value(llvm_value);

    return value_repr;
  }

  /* Register (store and track) a value representation. */
  ValueRepr*
  Context::register_value_repr(ValueRepr* value_repr)
  {
    return unit_context.register_value_repr(value_repr);
  }

  /* Drop a stored conversion for a value. */
  void
  Context::drop_value_repr(ValueRepr* value_repr)
  {
    unit_context.drop_value_repr(value_repr);
  }
    
  /* Look-up a stored conversion for a type, returning 0 if not
     found. */
  TypeRepr*
  Context::lookup_type(const llvm::Type* llvm_type)
  {
    TypeRepr* type_repr = 0;
    if (curr_context)
      type_repr = curr_context->lookup_type(llvm_type);
    if (!type_repr)
      type_repr = unit_context.lookup_type(llvm_type);

    return type_repr;
  }

  /* Register (store and track) a type representation. */
  TypeRepr*
  Context::register_type_repr(TypeRepr* type_repr)
  {
    return unit_context.register_type_repr(type_repr);
  }

  /* Drop a stored conversion for a type. */
  void
  Context::drop_type_repr(TypeRepr* type_repr)
  {
    unit_context.drop_type_repr(type_repr);
  }

  /* Store a value alias. */
  const llvm::Value* 
  Context::store_alias(const llvm::Value* llvm_value,
                       const llvm::Value* llvm_def)
  {
    return unit_context.scope.store_alias(llvm_value, llvm_def);
  }

  /* Look-up a value alias, return 0 if not found. */
  const llvm::Value* 
  Context::lookup_alias(const llvm::Value* llvm_value)
  {
    const llvm::Value* llvm_def = 0;

    if (curr_context)
      llvm_def = curr_context->scope.find_alias(llvm_value);
    if (!llvm_def)
      llvm_def = unit_context.scope.find_alias(llvm_value);

    return llvm_def;
  }

  /* Look-up a value alias, return 0 if not found. */
  ValueRepr* 
  Context::lookup_alias_repr(const llvm::Value* llvm_value)
  {
    ValueRepr* value_repr = 0;

    if (curr_context)
      value_repr = curr_context->scope.find_alias_repr(llvm_value);
    if (!value_repr)
      value_repr = unit_context.scope.find_alias_repr(llvm_value);

    return value_repr;
  }

  /* Store a value alias. */
  ValueRepr*
  Context::store_alias_repr(const llvm::Value* llvm_value,
                              ValueRepr* value_repr)
  {
    return unit_context.scope.store_alias_repr(llvm_value, value_repr);
  }


  /* 
   * For debugging llvmir. 
   */
  
  /* Print a generic tree, no pretty printing. */
  void
  dump_tree(tree decl)
  {
    debug_tree(decl);
  }

  /* Print a function declaration */
  void
  debug_print_function(tree fndecl)
  {
    const int dump_opts = 0;
    FILE* stream = stderr;

    dump_function_to_file(fndecl, stream, dump_opts);
  }

  /* Print an LLVM module. */
  void
  print_module(llvm::Module* llvm_module)
  {
    // Print the module
    llvm::raw_os_ostream llvm_cout (std::cout);
    llvm::ModulePass* llvm_module_printer = 0;

    llvm_module_printer = llvm::createPrintModulePass(&llvm_cout);
    llvm_module_printer->runOnModule (*llvm_module);
    delete llvm_module_printer;
  }

} // end of namespace llvmir

/* Functions with C linkage, for use by the GCC-frontend interface code. All
   functions are declared in llvmir-c.h. */

/* Option initialization. */
LLVMIR_EXTERN_C void
llvmir_initialize_options(struct gcc_options* gcc_opts)
{
  /* Initialize the options */
  llvmir::init_options(gcc_opts);
}

/* Process the options. This initializes the LLVMIR context and
   selects the parser to be used. */
LLVMIR_EXTERN_C void
llvmir_process_options()
{
  llvmir::process_options();
}

/* Compiler initialization. */
LLVMIR_EXTERN_C void
llvmir_initialize()
{
  llvmir::initialize();
}
  
/* Finalise front-end */
LLVMIR_EXTERN_C void
llvmir_finalize()
{
  llvmir::finalize();
}

/* Option setting */

/* Set the input language. */
LLVMIR_EXTERN_C bool
llvmir_option_lang(const char* arg, const int lang)
{
  return llvmir::option_lang(arg, lang);
}

/* Set the options to pass to the llvmir parser. */
LLVMIR_EXTERN_C bool
llvmir_option_opt(const char* opt_cstr)
{
  return llvmir::option_opt(opt_cstr);
}

/* Handle a -I option. */
LLVMIR_EXTERN_C bool
llvmir_option_include(const char* opt_cstr)
{
  return llvmir::option_include(opt_cstr);
}

/* Handle a -L option. */
LLVMIR_EXTERN_C bool
llvmir_option_libdir(const char* opt_cstr)
{
  return llvmir::option_libdir(opt_cstr);
}

/* Set amount of debugging information. */
LLVMIR_EXTERN_C bool
llvmir_option_dump(const char* opt_cstr,
                   const int value)
{
  return llvmir::option_dump(opt_cstr, value);
}

/* Generic debugging option. */
LLVMIR_EXTERN_C bool
llvmir_option_debug(const char* opt_cstr,
                    const int value)
{
  return llvmir::option_debug(opt_cstr, value);
}

/* Parsing */
LLVMIR_EXTERN_C bool
llvmir_parse_input_files(const char** filenames,
                         const unsigned int num_files,
                         int syntax_only)
{
  return llvmir::parse_input_files(filenames, num_files, syntax_only);
}

/* End of file. */

